#include "stdio.h"
#include "stdint.h"
#include "string.h"
#include "ctype.h"
#include "float.h"
#include "math.h"
#include "cJSON.h"
#include "mbedtls/base64.h"
#include "FeedPhy.h"
#include "FeedFrame.h"
#include "FeedID.h"
#include "FeedJson.h"
#include <strings.h>


int8_t cFeedJsonDecode(void *pvDatas, int32_t iSizeIn)
{
    cJSON *pjsonRoot;
    cJSON *pjsonItem[4];
    FeedFrameType typeFrame;
    int8_t cChannelLogic = -1;

    if((pjsonRoot = cJSON_Parse((char *)pvDatas)) == NULL)
        return -1;

    for(pjsonItem[0] = pjsonRoot->child; pjsonItem[0] != NULL; pjsonItem[0] = pjsonItem[0]->next)
    {
        /* 模组 */
        if(strcmp(pjsonItem[0]->string, "0") == 0)
            cChannelLogic = 0;
        /* 设备 */
        else if(strcmp(pjsonItem[0]->string, "1") == 0)
            cChannelLogic = 1;
        else
            continue;

        for(pjsonItem[1] = pjsonItem[0]->child; pjsonItem[1] != NULL; pjsonItem[1] = pjsonItem[1]->next)
        {
            if(strcasecmp(pjsonItem[1]->string, "ID") == 0)
            {
                for(pjsonItem[2] = pjsonItem[1]->child; pjsonItem[2] != NULL; pjsonItem[2] = pjsonItem[2]->next)
                {
                    if(strcasecmp(pjsonItem[2]->string, "set") == 0)
                        typeFrame.get = 0;
                    else if(strcasecmp(pjsonItem[2]->string, "get") == 0)
                        typeFrame.get = 1;
                    else
                        continue;

                    for(pjsonItem[3] = pjsonItem[2]->child; pjsonItem[3] != NULL; pjsonItem[3] = pjsonItem[3]->next)
                    {
                        /* 转换成 自定义格式 */
                        if(cFeedJsonToFrame(&typeFrame, pjsonItem[3]) != 0)
                            continue;

                        typeFrame.response = 0;
                        typeFrame.needResponse = 1;

                        /* 解析 数据 */
                        if(cChannelLogic == 0)
                        {
                            cFeedIDDecode(&typeFrame);
                        }
                        /* 转发 数据 */
                        else
                        {
                            cFeedPhyDatasAdd(&typeFrame, (typeFrame.get == 1) ? 2 : typeFrame.length + 4);
                        }
                    }
                }
            }
        }

        break;
    }

    cJSON_Delete(pjsonRoot);

    return cChannelLogic;
}

/* securely comparison of double-point variables */
static int8_t compare_double(double a, double b)
{
    double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b);
    return (fabs(a - b) <= maxVal * DBL_EPSILON);
}

int32_t cFeedJsonToFrame(void *pvFrame, void *pvJson)
{
    FeedFrameType *ptypeFrame = (FeedFrameType *)pvFrame;
    cJSON *pjsonRoot = (cJSON *)pvJson;
    double dValue;
    float fValue;
    uint32_t uiValue;
    size_t size = 0;
    uint8_t number_buffer[16];

    ptypeFrame->ID = (pjsonRoot->string != NULL) ? atoi(pjsonRoot->string) : (strcasecmp(pjsonRoot->valuestring, "All") == 0) ? 0xFFF : atoi(pjsonRoot->valuestring);

    if(pjsonRoot->string == NULL)
    {
        ptypeFrame->length = 0;
        ptypeFrame->type = FEED_FRAME_TYPE_INT;
    }
    else if(pjsonRoot->type == cJSON_Number)
    {
        if((double)pjsonRoot->valueint == pjsonRoot->valuedouble)
        {
            for(uiValue = pjsonRoot->valueint; ((uiValue != 0) || (size == 0)); uiValue >>= 8)
            {
                ptypeFrame->datas[size++] = uiValue;
            }

            ptypeFrame->length = size;
            ptypeFrame->type = FEED_FRAME_TYPE_INT;
        }
        else
        {
            fValue = pjsonRoot->valuedouble;

            /* Try 6 decimal places of precision to avoid nonsignificant nonzero digits */
            sprintf((char *)number_buffer, "%1.7g", (double)fValue);

            /* Check whether the original double can be recovered */
            if ((sscanf((char *)number_buffer, "%lg", &dValue) != 1) || !compare_double(dValue, pjsonRoot->valuedouble))
            {
                ptypeFrame->length = 8;
                memcpy(ptypeFrame->datas, &pjsonRoot->valuedouble, ptypeFrame->length);
            }
            else
            {
                ptypeFrame->length = 4;
                memcpy(ptypeFrame->datas, &fValue, ptypeFrame->length);
            }

            ptypeFrame->type = FEED_FRAME_TYPE_FLOAT;
        }
    }
    else if(pjsonRoot->type == cJSON_String)
    {
        /* cJSON_Raw */
        if(strncmp(pjsonRoot->valuestring, ":raw:", 5) == 0)
        {
            mbedtls_base64_decode(ptypeFrame->datas, sizeof(ptypeFrame->datas), &size, (uint8_t *)&pjsonRoot->valuestring[5], strlen(pjsonRoot->valuestring) - 5);
            ptypeFrame->length = size;
            ptypeFrame->type = FEED_FRAME_TYPE_RAW;
        }
        /* cJSON_String */
        else
        {
            ptypeFrame->length = strlen(pjsonRoot->valuestring) > sizeof(ptypeFrame->datas) ? sizeof(ptypeFrame->datas) : strlen(pjsonRoot->valuestring);
            memcpy(ptypeFrame->datas, pjsonRoot->valuestring, ptypeFrame->length);
            ptypeFrame->type = FEED_FRAME_TYPE_STRING;
        }
    }
    else
    {
        return 1;
    }

    return 0;
}

int32_t iFeedFrameToJson(void *pvJson, void *pvFrame)
{
    cJSON **ppjsonRoot = (cJSON **)pvJson;
    FeedFrameType *ptypeFrame = (FeedFrameType *)pvFrame;
    uint8_t *pucBase64;
    double dValue;
    int64_t lValue;
    float fValue;
    int32_t iValue, i;
    size_t size;

    if((ptypeFrame->type == FEED_FRAME_TYPE_INT) || (ptypeFrame->type == FEED_FRAME_TYPE_FLOAT))
    {
        if(ptypeFrame->length > 8)
            return -1;

        if(ptypeFrame->type == FEED_FRAME_TYPE_FLOAT)
        {
            memcpy(((ptypeFrame->length > 4) ? &dValue : &fValue), ptypeFrame->datas, ptypeFrame->length);

            dValue = ptypeFrame->length > 4 ? dValue : fValue;
        }
        else
        {
            for(lValue = iValue = 0, i = 0; i < ptypeFrame->length; ++i)
            {
                lValue |= (uint64_t)ptypeFrame->datas[i] << (8 * i);
                iValue |= (uint32_t)ptypeFrame->datas[i] << (8 * i);
            }

            dValue = ptypeFrame->length > 4 ? lValue : iValue;
        }

        *ppjsonRoot = cJSON_CreateNumber(dValue);

        return 0;
    }
    else if(ptypeFrame->type == FEED_FRAME_TYPE_STRING)
    {
        *ppjsonRoot = cJSON_CreateString((char *)ptypeFrame->datas);

        return 0;
    }
    else
    {
        size = (ptypeFrame->length * 4) / 3 + 4 + 5;
        if((pucBase64 = calloc(size, sizeof(uint8_t))) != NULL)
        {
            memcpy(pucBase64, ":raw:", 5);

            mbedtls_base64_encode(&pucBase64[5], size - 5, &size, ptypeFrame->datas, ptypeFrame->length);
            *ppjsonRoot = cJSON_CreateString((char *)pucBase64);

            free(pucBase64);
        }

        return 0;
    }
}
